
#include <BRange>
#include <BStyle>
#include <BFont>
#include <BEvent>
#include <BPainter>
#include <BKeyEvent>
#include <BEvent>
#include <BMouseEvent>
#include <BSystem>

#include "member_BDateEdit.h"
#include "member_BObject.h"
#include "member_BStyle.h"

using namespace BWE;

#define member					(*(member_BDateEdit*)_ptr)
#define member_allocate()		_ptr = new member_BDateEdit(this)
#define member_release()		delete (member_BDateEdit*)_ptr

BDateEdit::BDateEdit(BWidget* parent) : BWidget(parent)
{
	member_allocate();
}
BDateEdit::BDateEdit(const BDate& date, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setDate(date);
}
BDateEdit::~BDateEdit()
{
	member_release();
}

void BDateEdit::setDate(const BDate& date)
{
	if (member.date != date)
	{
		member.date = date;
		member.dirtyText = true;
		emit(Signal_DateChanged, member.date);
		this->fresh();
	}
}
const BDate& BDateEdit::date() const
{
	return member.date;
}

void BDateEdit::setFormat(const BString& format)
{
	if (member.format != format)
	{
		member.format = format;
		for (int i = 0; i < member.format.size(); i++)
		{
			char c = member.format[i];
			if (c >= '0' && c <= '9')
				member.format.remove(i--);
		}
		member.format.lower();
		member.dirtyText = true;
		object_member(this)->check_emit(Signal_FormatChanged, format);
		this->fresh();
	}
}
const BString& BDateEdit::format() const
{
	return member.format;
}

void BDateEdit::setAlign(Align align)
{
	if (member.align != align)
	{
		member.align = align;
		member.dirty = true;
		this->fresh();
	}
}
Align BDateEdit::align() const
{
	return member.align;
}

void BDateEdit::setReadOnly(bool readOnly)
{
	if (member.readOnly != readOnly)
	{
		member.readOnly = readOnly;
		if (readOnly)
			this->setCursor(Cursor_None);
		else if (this->focusable())
			this->setCursor(Cursor_Ibeam);
		this->emit(Signal_ReadOnly, readOnly);
		this->fresh();
	}
}
bool BDateEdit::readOnly() const
{
	return member.readOnly;
}

void BDateEdit::setSynchro(bool synchro)
{
	if (member.synchro != synchro)
	{
		member.synchro = synchro;
		if (synchro)
			this->setCursor(Cursor_None);
		else if (this->focusable())
			this->setCursor(Cursor_Ibeam);
		this->fresh();
	}
}
bool BDateEdit::synchro() const
{
	return member.synchro;
}

void BDateEdit::setTextFormat(BTextFormat* textFormat)
{
	if (member.block.format != textFormat)
	{
		member.block.format = textFormat;
		if (textFormat)
		{
			textFormat->styled(this->realStyle());
			textFormat->changed(member.block.text);
		}
		this->emit(Signal_FormatChanged, textFormat);
	}
}
const BTextFormat* BDateEdit::textFormat() const
{
	return member.block.format;
}
BTextFormat* BDateEdit::textFormat()
{
	return member.block.format;
}

BSize BDateEdit::sizeHint() const
{
	if (member.dirtyText && !member.editing)
	{
		member.date.toString(member.block.text, member.format.cstr());
		member.block.reset();
		member.dirtyText = false;
		member.dirty = true;
	}
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	BSize textSize = member.block.textSize;
	return this->preferSize(textSize);
}

void BDateEdit::resizeEvent(const BEvent& event)
{
	member.dirty = true;
	this->fresh();
}
void BDateEdit::updateEvent(const BEvent& event)
{
	if (member.synchro)
	{
		BDate date = BSystem::LocalDate();
		if (member.date != date)
		{
			member.date = date;
			member.dirtyText = true;
			emit(Signal_DateChanged, member.date);
			this->fresh();
		}
	}
	if (this->focused() && member.block.count == 0)
	{
		bool blinkShown = true;
		if (member.blinkTime > 0)
		{
			BReal now = event.time();
			BReal time = now - member.blinkTick;
			blinkShown = time <= member.blinkTime / 2;
			if (time > member.blinkTime)
				member.blinkTick = now;
		}
		if (member.blinkShown != blinkShown)
		{
			member.blinkShown = blinkShown;
			this->fresh();
		}
	}
}

void BDateEdit::styleEvent(const BEvent& event)
{
	BWidget::styleEvent(event);

	const BStyle* style = this->realStyle();

	style_member(style)->assign(Value_Align, member.align);
	if (style_member(style)->assign(Value_Format, member.format))
		member.format.lower();

	if (member.block.format)
		member.block.format->styled(style);

	member.block.font = this->realFont();
	member.block.dirty = true;
	member.dirty = true;
}
void BDateEdit::freshEvent(const BEvent& event)
{
	if (member.dirtyText && !member.editing)
	{
		member.date.toString(member.block.text, member.format.cstr());
		member.block.reset();
		if (member.block.format)
			member.block.format->changed(member.block.text);
		member.dirtyText = false;
		member.dirty = true;
	}
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	BWidget::freshEvent(event);
}
void BDateEdit::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	painter.drawForeground();

	State state = painter.state();
	if (this->focused() && member.block.index >= 0 && member.block.count)
	{
		const BRect& crect = this->centerRect();
		BPoint pos = member.block.rect.pos();
		painter.setColor(Color_Selection, state);
		int begin = bMin(member.block.index, member.block.index + member.block.count);
		int end = bMax(member.block.index, member.block.index + member.block.count);
		for (int i = begin; i < end; i++)
		{
			const ICodeCell& cell = member.block.cells[i];
			BRect cellRect = cell.rect + pos;
			painter.fillRect(cellRect);
		}
	}

	if (painter.setColor(Color_Text, state))
	{
		member.block.paint(painter);
	}

	painter.drawBorder();
	painter.drawPerch();
	painter.drawMargin();

	if (!member.readOnly && !member.synchro)
	{
		if (member.blinkShown && member.block.count == 0)
		{
			painter.setColor(Color_Indicator);
			BLine2 line = member.inputLine();
			painter.drawLine(line.begin(), line.end());
		}
	}
}

void BDateEdit::focusEvent(const BEvent& event)
{
	if (this->focused())
	{
		member.focusedTime = event.time();
		member.block.index = 0;
		member.block.count = member.block.cells.size();
		this->fresh();
	}
	else
	{
		if (member.editing)
		{
			member.editing = false;
			member.dirtyText = true;
			emit(Signal_EditFinished, member.date);
		}
		member.blinkShown = false;
		member.dirty = true;
		this->fresh();
	}
	BWidget::focusEvent(event);
}
void BDateEdit::inputEvent(const BEvent& event)
{
	if (!member.readOnly && !member.synchro)
	{
		BCode code = event.value();
		if ((code >= '0' && code <= '9'))
		{
			if (member.block.count > 0)
			{
				member.removeSelection();
			}
			if (member.block.insert(code))
			{
				member.textEdited();
				BLine2 line = member.inputLine();
				event.accept(line.end());
				this->fresh();
			}
		}
	}
	BWidget::inputEvent(event);
}

void BDateEdit::keyPressEvent(const BKeyEvent& keyEvent)
{
	Key key = keyEvent.key();
	if (keyEvent.keyState(Key_Ctrl))
	{
		if (key == Key_C)
		{
			BString text;
			if (member.block.index >= 0)
			{
				if (member.block.count)
					member.block.collect(text);
				else
					text = member.block.text;
			}
			BSystem::SetClippedText(text);
			keyEvent.accept(true);
		}
		if (key == Key_A)
		{
			member.block.index = 0;
			member.block.count = member.block.cells.size();
			member.blinkShown = false;
			this->fresh();
			keyEvent.accept(true);
		}
	}
	else if (!member.readOnly && !member.synchro)
	{
		if (key == Key_Enter)
		{
			keyEvent.accept();
			if (member.editing)
			{
				member.editing = false;
				member.dirtyText = true;
				member.dirty = true;
				emit(Signal_EditFinished, member.date);
			}
			member.blinkShown = false;
			this->fresh();
		}
		if (key == Key_Left)
		{
			if (member.block.count > 0)
			{
				member.block.count = 0;
				this->fresh();
			}
			else if (member.block.index > 0)
			{
				member.block.index--;
				this->fresh();
			}
			keyEvent.accept();
		}
		if (key == Key_Right)
		{
			if (member.block.count > 0)
			{
				member.block.index += member.block.count;
				member.block.count = 0;
				this->fresh();
			}
			else if (member.block.index < member.block.cells.size())
			{
				member.block.index++;
				this->fresh();
			}
			keyEvent.accept();
		}
		if (key == Key_Back)
		{
			if (member.block.count)
			{
				if (member.removeSelection())
				{
					member.textEdited();
				}
			}
			else if (member.block.index > 0)
			{
				member.block.index--;
				if (member.removeCode())
				{
					member.textEdited();
				}
			}
			member.blinkTick = keyEvent.time() + (BReal)0.5;
			keyEvent.accept();
		}
		if (key == Key_Delete)
		{
			if (member.block.count)
			{
				if (member.removeSelection())
				{
					member.textEdited();
				}
			}
			else
			{
				if (member.removeCode())
				{
					member.textEdited();
				}
			}
			member.blinkTick = keyEvent.time() + (BReal)0.5;
			keyEvent.accept();
		}
	}
	BWidget::keyPressEvent(keyEvent);
}
void BDateEdit::keyReleaseEvent(const BKeyEvent& keyEvent)
{
	Key key = keyEvent.key();
	if (key == Key_Left)
	{
		keyEvent.accept();
		this->fresh();
	}
	if (key == Key_Right)
	{
		keyEvent.accept();
		this->fresh();
	}
	if (key == Key_Back)
	{
		keyEvent.accept();
		this->fresh();
	}
	if (key == Key_Delete)
	{
		keyEvent.accept();
		this->fresh();
	}
	if (key == Key_Enter)
	{
		keyEvent.accept();
		this->fresh();
	}
	BWidget::keyReleaseEvent(keyEvent);
}

void BDateEdit::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverIndex != -1)
	{
		member.hoverIndex = -1;
		this->fresh();
	}
	BWidget::mouseEnterEvent(mouseEvent);
}
void BDateEdit::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverIndex != -1)
	{
		member.hoverIndex = -1;
		this->fresh();
	}
	BWidget::mouseLeaveEvent(mouseEvent);
}

void BDateEdit::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.time() - member.focusedTime < (BReal)0.3)
		return;
	if (mouseEvent.button() == Button_Left)
	{
		const BPoint& pos = mouseEvent.pos();
		if (member.block.count > 0 || member.block.index != member.hoverIndex)
		{
			member.block.index = member.hoverIndex;
			member.block.count = 0;
			this->fresh();
		}
		member.pressed = true;
		member.pressedIndex = member.hoverIndex;
		mouseEvent.accept();
	}
	BWidget::mousePressEvent(mouseEvent);
}
void BDateEdit::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		member.pressed = false;
		mouseEvent.accept();
		this->fresh();
	}
	BWidget::mouseReleaseEvent(mouseEvent);
}
void BDateEdit::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	const BPoint& pos = mouseEvent.pos();
	int index = member.block.indexAt(pos);
	if (member.hoverIndex != index)
	{
		member.hoverIndex = index;
	}
	if (member.pressed)
	{
		member.block.index = bMin(member.pressedIndex, member.hoverIndex);
		member.block.count = abs(member.pressedIndex - member.hoverIndex);
		this->fresh();
	}
	mouseEvent.accept();
	BWidget::mouseMoveEvent(mouseEvent);
}
void BDateEdit::mouseWheelEvent(const BMouseEvent& mouseEvent)
{
	if (!member.readOnly && !member.synchro)
	{
		int whell = mouseEvent.wheel();
		BDate date = member.date + whell;
		this->setDate(date);
		emit(Signal_DateEdited, member.date);
		member.editing = false;
		this->fresh();
		mouseEvent.accept();
	}
	BWidget::mouseWheelEvent(mouseEvent);
}
void BDateEdit::mouseDoubleClickEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		if (member.block.cells.check(member.hoverIndex))
		{
			member.block.index = member.hoverIndex;
			member.block.count = 0;
			member.block.selectWord();
		}
		else
		{
			member.block.index = 0;
			member.block.count = member.block.cells.size();
		}
		mouseEvent.accept();
		this->fresh();
	}
	BWidget::mouseDoubleClickEvent(mouseEvent);
}

